{
  Copyright 2012 Sergey Ostanin

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
}

unit InputQuestionEditor;

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  ComCtrls, ActnList, Menus, Shape, PadEditor, Windows, PatternEditor,
  superobject, MiscUtils, VisualUtils, QuestionEditors, Questions, InputQuestion;

type
  TInputQuestionEditorFrame = class(TShapeFrame, IQuestionEditor)
    actAddTextPattern: TAction;
    actAddRegexpPattern: TAction;
    actDeletePattern: TAction;
    actMovePatternDown: TAction;
    actMovePatternUp: TAction;
    alQuestion: TActionList;
    ilQuestion: TImageList;
    miAddRegexpPattern: TMenuItem;
    miAddTextPattern: TMenuItem;
    pnlQuestion: TPanel;
    pmAddPattern: TPopupMenu;
    sbxQuestion: TScrollBox;
    tbrQuestion: TToolBar;
    tbAdd: TToolButton;
    tbDeletePattern: TToolButton;
    tbSeparator1: TToolButton;
    tbMovePatternUp: TToolButton;
    tbMovePatternDown: TToolButton;
    procedure actDeletePatternExecute(Sender: TObject);
    procedure actDeletePatternUpdate(Sender: TObject);
    procedure actMovePatternDownExecute(Sender: TObject);
    procedure actMovePatternDownUpdate(Sender: TObject);
    procedure actMovePatternUpExecute(Sender: TObject);
    procedure actMovePatternUpUpdate(Sender: TObject);
  private
    FFormulation: TPadEditorFrame;
    FPatterns: TPatternEditorFrameList;
    FOldPatternEditorIds: UnicodeString;
    FSelectPattern: Integer;
    procedure SyncPatterns;
    procedure SelectPatternType(AddPatternAction: TAction);
    procedure AddPatternExecute(Sender: TObject);
    procedure AddPatternUpdate(Sender: TObject);
    function SelectedPatternIndex: Integer;
    procedure MovePattern(Down: Boolean);
    { private declarations }
  public
    procedure AfterConstruction; override;
    destructor Destroy; override;
    procedure Render;
    { public declarations }
  end; 

implementation

{$R *.lfm}
{$R *.rc}

resourcestring
  SInputQuestion = 'Text Input Question';

const
  ADD_PATTERN_COMMANDS: array [0..1] of String = ('addTextPattern', 'addRegexpPattern');

{ TInputQuestionEditorFrame }

procedure TInputQuestionEditorFrame.actDeletePatternExecute(Sender: TObject);
var
  Index: Integer;
  Input: ISuperObject;
begin
  Index := SelectedPatternIndex;

  Input := SO;
  Input.I['index'] := Index;
  Dispatcher.PerformAction(Id, 'deletePattern', Input);

  if Index >= FPatterns.Count-1 then
    Dec(Index);
  if Index >= 0 then
    FSelectPattern := Index;
end;

procedure TInputQuestionEditorFrame.actDeletePatternUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled := SelectedPatternIndex <> -1;
end;

procedure TInputQuestionEditorFrame.actMovePatternDownExecute(Sender: TObject);
begin
  MovePattern(TRUE);
end;

procedure TInputQuestionEditorFrame.actMovePatternDownUpdate(Sender: TObject);
var
  Index: Integer;
begin
  Index := SelectedPatternIndex;
  (Sender as TAction).Enabled := (Index <> -1) and (Index < FPatterns.Count-1);
end;

procedure TInputQuestionEditorFrame.actMovePatternUpExecute(Sender: TObject);
begin
  MovePattern(FALSE);
end;

procedure TInputQuestionEditorFrame.actMovePatternUpUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled := SelectedPatternIndex > 0;
end;

procedure TInputQuestionEditorFrame.AfterConstruction;
var
  Item: TMenuItem;
begin
  inherited;
  alQuestion.OnExecute := Scene.ActionListExecuteFilter;
  LoadActionImages(alQuestion);
  MakeVerticallyAutoScrollable(sbxQuestion);

  for Item in pmAddPattern.Items do
  begin
    Item.Action.OnExecute := AddPatternExecute;
    Item.Action.OnUpdate := AddPatternUpdate;
  end;
  SelectPatternType(actAddTextPattern);

  FFormulation := TPadEditorFrame.Create(Shape.S['formulationEditor'], Dispatcher, pnlQuestion);
  FFormulation.BorderSpacing.Bottom := pnlQuestion.ChildSizing.VerticalSpacing + 4;
  FFormulation.Border := TRUE;

  FPatterns := TPatternEditorFrameList.Create;
  FSelectPattern := -1;
end;

destructor TInputQuestionEditorFrame.Destroy;
begin
  FreeAndNil(FFormulation);
  FreeAndNil(FPatterns);
  inherited;
end;

procedure TInputQuestionEditorFrame.Render;
var
  Pattern: TPatternEditorFrame;
begin
  UpdateShape;

  DisableAutoSizing;
  try
    FFormulation.Render;
    SyncPatterns;
    for Pattern in FPatterns do
      Pattern.Render;
  finally
    EnableAutoSizing;
  end;

  if (FSelectPattern >= 0) and (FSelectPattern < FPatterns.Count) then
    FPatterns[FSelectPattern].SelectFirst;
  FSelectPattern := -1;
end;

procedure TInputQuestionEditorFrame.SyncPatterns;
var
  Ids, Id: ISuperObject;
begin
  Ids := Shape['patternEditor'];
  if AssignDiffering(Ids.AsJSon, FOldPatternEditorIds) then
  begin
    FPatterns.Clear;
    for Id in Ids do
      FPatterns.AddSafely(TPatternEditorFrame.Create(Id.AsString, Dispatcher, pnlQuestion));
  end;
end;

procedure TInputQuestionEditorFrame.SelectPatternType(AddPatternAction: TAction);
var
  Item: TMenuItem;
begin
  for Item in pmAddPattern.Items do
  begin
    (Item.Action as TAction).ShortCut := 0;
    Item.Default := Item.Action = AddPatternAction;
  end;
  AddPatternAction.ShortCut := VK_F5;
  tbAdd.Action := AddPatternAction;
end;

procedure TInputQuestionEditorFrame.AddPatternExecute(Sender: TObject);
var
  a: TAction;
begin
  a := Sender as TAction;
  SelectPatternType(a);
  Dispatcher.PerformAction(Id, ADD_PATTERN_COMMANDS[a.Tag]);
  FSelectPattern := FPatterns.Count;
end;

procedure TInputQuestionEditorFrame.AddPatternUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled := FPatterns.Count < Shape.I['maxPatterns'];
end;

function TInputQuestionEditorFrame.SelectedPatternIndex: Integer;
begin
  for Result := 0 to FPatterns.Count-1 do
    if ContainsActiveFormControl(FPatterns[Result]) then
      Exit;
  Result := -1;
end;

procedure TInputQuestionEditorFrame.MovePattern(Down: Boolean);
var
  Input: ISuperObject;
begin
  Input := SO;
  Input.I['index'] := SelectedPatternIndex;
  Input.B['forward'] := Down;
  Dispatcher.PerformAction(Id, 'movePattern', Input);
end;

initialization

  QuestionRegistry.Add(TInputQuestion.Kind, SInputQuestion, 'InputQuestionEditor/inputQuestion.png', '');
  QuestionEditorRegistry.Add(TInputQuestionEditorFrame, $d89920ae02c20e75);

end.

